package com.ypf.accesslimit.limit;

import com.ypf.accesslimit.config.ConfigInfo;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;

@Slf4j
@Aspect
@Component
public class LimitAspect {
    @Autowired
    private AccessLimiter limiter;

    @Autowired
    private GenerateRedisKey generateRedisKey;

    @Autowired ConfigInfo configInfo;

    /**
     * 切入点---实际被增强的方法
     * 定义一个切入点 所有被@Limit所修饰的方法 会被织入advice
     */
    @Pointcut("@annotation(com.ypf.accesslimit.limit.Limit)")
    public void limitPointcut(){

    }

    /**
     * redisKey中保存的是相关的限流政策  而bindingKey中保存的该接口访问的次数
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("limitPointcut()")
    public Object doArround(ProceedingJoinPoint joinPoint) throws Throwable {
        String redisKey = generateRedisKey.getMethodUrlConvertRedisKey(joinPoint);
        System.out.println("redisKey = " + redisKey);
        HashMap map = limiter.getLimitInfo(redisKey);
        if (map!=null){  // 判断
            String r_times = (String) map.get("times");
            Long times = Long.parseLong(r_times);

            String r_per = (String) map.get("per");
            Long per = Long.parseLong(r_per);

            TimeUnit unit = (TimeUnit) map.get("unit");


            String bindingKey = genBindingKey(joinPoint);
            System.out.println("bindingKey = " + bindingKey);
            Boolean isLimit = limiter.isLimited(bindingKey,times,per,unit);
            if (isLimit){
                // 实际应该抛出异常阻止访问  这里只是模仿效果就不写异常类 和异常捕获类了
                System.out.println("接口已被限制访问");
            }

         }

        return joinPoint.proceed(); // 方法开始执行
    }

    /**
     * 根据不同微服务的limit.id(配置文件中写入) 来生成不同机器不同接口的唯一限制key
     * @param joinPoint
     * @return
     */
    private String genBindingKey (ProceedingJoinPoint joinPoint){
        try {
            Method m =((MethodSignature)joinPoint.getSignature()).getMethod();
            return  configInfo.getId()+"-"+joinPoint.getTarget().getClass().getName()+"."+m.getName();
        }catch (Throwable e){
            return null;
        }
    }
}
